Css Module记录

css-modules: https://github.com/css-modules/css-modules

2021.1.11 星期二 :

实际

语法

用了css module 就不可以通过className 获取到dom元素了。因为编译后className 添加上了module(有hash,可以通过配置定义)。

1
2
3
4
5
  <View className={styles.content}>
<Textarea placeholder="请输入" value={replyMsg} className={styles[`reply_area-${process.env.TARO_ENV}`]} onInput={setAreaValue} maxlength={150}></Textarea>

// 转化后
<taro-view-core class="index-module__content___2NdXC hydrated">

指定文件

可以通过webpack-loader test 指定特殊的文件使用css module。比如*.module.scss。
也可以配置loader的include/exclude。

三方组件/嵌套组件

解决嵌套组件样式不生效问题:
1)在类名外嵌套:global,凡是这样声明的class,在样式表被编译时都不会被编译成哈希字符串,即类名会保持原样,即可作用于引用的antd组件或未使用样式表的自定义组件类名。

使用:global来修改. 但是这样会将所有的模态框中的content都设为透明的,而我只想要单独设置这一个模态框的样式而已,所以我们可以在这个类的上层再加一层用于识别(跟我们css权重应用一样的道理)。

2)样式表里用 元素选择器,会对该样式表下的所有(包括组件内)的元素生效

1
2
3
4
5
6
7
8
9
10
11
12
13
<div className={styles.root}>
<div>no class (这里是原生元素div)</div>
<div className='ant-list-item'>no style (这里是原生元素div)</div>
<div className={styles['ant-list-item']}>has styles (这里是原生元素div)</div>
<List bordered style={{marginTop: '30px', border: '1px solid rgb(34, 177, 76)'}}>
{demoList.map(item => (
<Item key={item.id}>
{item.text}
</Item>
))}
</List>
<MyComponent></MyComponent>
</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.root {
background-color: #fff;
padding: 24px 32px;
border-radius: 4px;

// 元素选择器编译后还是元素,不带哈希值所以还是能影响组件内部组件的样式
div {
background-color: red;
}

// 编译后 ant-list-item; global下的(包括嵌套)类名都不会带上哈希字符串
// 相当于
// .root___xxxx .ant-list-item {
// background-color: red;
// }
:global {
.ant-list-item {
background-color: red;
}
}
}

\$_PS: 实际使用中,只有在第一个全局的global下的样式才生效。
担心组件的修改会对全局造成影响,可以把样式写在文件中<style>{.taro-webview{position: relative;}}</style>

1
2
3
4
5
6
7
8
9
// <Dialog className={styles.dialog_coupon} ref={refDialog}
<Dialog className="dialog_coupon" ref={refDialog}
title="规则"
>
<WebView src={Data.ruleUrl}/>
</Dialog>

/* # Dialog 公共组件。已经预留了content类名 */
<View className={`${styles.content} content`}></View>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* # 1 无效 */
/* .dialog_couple {
// height: 310*2px;
// max-height: 310*2px;
:global {
.content {
height: 310*2px;
max-height: 310*2px;
}
.taro-webview {
position: relative;
}
}
} */

:global {
/* # 2 有效。只有在全局,而且没有多余的类名。 */
// Dialog 组件中已经下发了className=
.content {
max-height: 310*2px;
height: 620px;
}

.taro-webview {
position: relative!important;
}
/* # 3 无效。想要通过dialog_couple 增加权重 */
/* .dialog_couple {
.content {
height: 310*2px;
max-height: 310*2px;
max-height: 620px;
height: 620px;
}

.taro-webview {
position: relative;
}
} */
}

其它

在处理动画animation的关键帧keyframes,动画名称必须先写:
比如,animation: deni .5s,能正常编译; animation: .5s deni, 则编译异常

加强

weapp-css-modules

极致追求,让小程序代码包立减 10% 的插件 weapp-css-modules

css-modules 是一种 css 模块化方案,它在构建过程中生成一个原类名与新类名的 map,根据 map 引用样式,通过设定 hash 规则,实现了对 CSS 类名作用域的限定,它通常用来解决页面类名冲突的问题。由于微信小程序内组件样式默认隔离,为什么要使用 css-modules 呢?
有以下 2 个原因:

  • hash 化后可以实现更短的命名,减少代码包体积
  • 跨端项目需要兼顾非小程序环境,避免样式冲突

weapp-css-modules 做了哪些事?

  • 新类名单字母编排,减少代码量
  • 移除类名映射 map,替换 js 和 wxml 中变量为编译后类名

目前只开发了适用于使用 gulp 编译小程序的 gulp 插件,后续计划开发 webpack 可用的插件实现相同功能。

react-css-modules

react-css-modules
如果你不想频繁的输入 styles.**,有一个 GitHub - gajus/react-css-modules: Seamless mapping of class names to CSS modules inside of React components.,它通过高阶函数的形式来生成 className,不过不推荐使用,后文会提到。
API 也很简单,给组件外包一个 CSSModules 即可。

babel-plugin-react-css-modules

不过这样我们可以看到,它是需要运行时的依赖,而且需要在运行时才获取 className,性能损耗大,那么有没有方便又接近无损的方法呢?答案是有的,使用 babel 插件 babel-plugin-react-css-modules GitHub - gajus/babel-plugin-react-css-modules: Transforms styleName to className using compile time CSS module resolution. 把 className 获取前置到编译阶段。
babel-plugin-react-css-modules
babel-plugin-react-css-modules 可以实现使用 styleName 属性自动加载 CSS 模块。我们通过该 babel 插件来进行语法树解析并最终生成 className。
来看看组件的写法,现在你只需要把 className 换成 styleName 即可获得 CSS 局部作用域的能力了,是不是非常简单。

1
2
3
4
5
6
7
8
9
10
11
import React from 'react';
import styles from './table.css';

class Table extends React.Component {
render () {
return <div styleName='table'>
</div>;
}
}

export default Table;

CSS Modules介绍

# CSS Modules
TODO: yingxiang

为什么引入CSS Modules

(1)全局样式冲突
webpack进行打包时,将所有js文件导入到入口App.js文件中,样式也会统一加载到入口中,根据css的layout规则,后面的样式会覆盖掉前面的样式声明,造成全局样式的覆盖问题。
(2)嵌套层次过深的选择器
为了解决全局样式的冲突问题,不得不引入一些特地命名namespace来区分,但是往往有些namespace命名得不够清晰,就会造成要想下一个样式不会覆盖,就要再加一个新的命名空间来进行区分,最终可能一个元素的显示样式嵌套特别深。

嵌套特别深会造成的问题:

  • 根据CSS选择器的解析规则可以知道,层级越深,比较的次数也就越多,影响整个页面的渲染
  • 增加了不必要的字节开销
  • 语义混乱 可扩展性不好,约束越多,扩展性越差
    (3)无法共享变量
    复杂组件要使用 JS 和 CSS 来共同处理样式

一些解决方案

(1)css预处理器(less/sass) 支持模块引入
存在问题:不能解决全局样式冲突问题
2)BEM(Block Element Modifier)解决命名冲突以及更好的语义化
存在问题:对于嵌套过深的层次在命名上会给需要语义化体现的元素造成很大的困难 对于多人协作上,需要统一命名规范,这同样也会造成额外的effort

CSS Modules

(1)什么是css modules
通过构建工具来使指定class达到scope的过程

(2)css modules优势
解决全局命名冲突问题 css modules只关心组件本身 命名唯一
模块化 可以使用composes来引入自身模块中的样式以及另一个模块的样式
解决嵌套层次过深的问题 使用扁平化的类名

(3)css modules的用法
开启css modules功能

(4)其他用法
局部变量和全局变量
compose组合class
CSS和JS变量共享

备注

项目中如果使用antd 需要将antd样式不进行编译
项目中使用stylelint进行css检查 需要在stylelint配置忽略css-modules的关键字

css moudule 对比

和scoped(Vue.js)
CSS Modules 与 scoped 的不一样

Vue中scoped css和css module比较
Vue中scoped css和css module比较

knowledge is no pay,reward is kindness
0%